VYPR
Medium severity4.5NVD Advisory· Published May 29, 2026· Updated May 29, 2026

CVE-2026-44640

CVE-2026-44640

Description

NanoMQ MQTT Broker (NanoMQ) is an all-around Edge Messaging Platform. Prior to 0.24.14, aio->prov_data is stored as nni_quic_conn* during dialing, but read as ex_quic_conn* during dialer close. This type confusion causes invalid object interpretation and leads to close-path hang/crash behavior. This vulnerability is fixed in 0.24.14.

AI Insight

LLM-synthesized narrative grounded in this CVE's description and references.

NanoMQ prior to 0.24.14 has a type confusion in QUIC dialer close that can cause hang or crash.

Vulnerability

In NanoMQ versions prior to 0.24.14, the QUIC dialer close path suffers from a type confusion bug. During dialing, aio->prov_data is stored as a nni_quic_conn* pointer, but during dialer close it is read as ex_quic_conn*. This mismatch leads to invalid object interpretation. The vulnerable code is in nng/src/supplemental/quic/msquic_dial.c [2]. Affected versions: all before 0.24.14.

Exploitation

An attacker can trigger this vulnerability by initiating a QUIC dialer connection to a target and then immediately closing the dialer. The provided PoC demonstrates this: allocate a dialer, start a dial, then close the dialer. No authentication or special privileges are required; the attacker only needs network access to the broker's QUIC endpoint. The race window is minimal as the close happens immediately after dial start.

Impact

Successful exploitation results in a hang or crash of the NanoMQ broker process. This constitutes a denial-of-service (DoS) condition. The type confusion can cause memory corruption, leading to undefined behavior. No remote code execution or data disclosure is indicated in the advisory.

Mitigation

The vulnerability is fixed in NanoMQ version 0.24.14, released on 2026-05-29 [1]. Users should upgrade to this version or later. No workarounds are provided. The advisory does not list this CVE in CISA KEV.

AI Insight generated on May 29, 2026. Synthesized from this CVE's description and the cited reference URLs; citations are validated against the source bundle.

Affected products

2
  • Nanomq/Nanomqreferences2 versions
    (expand)+ 1 more
    • (no CPE)
    • (no CPE)range: <0.24.14

Patches

2
bf989bd39cd3

* FIX [rest_api] Free `data_info` before returning parameter errors and valid `proto_ver`

https://github.com/emqx/nanomqAlvinMay 1, 2026Fixed in 0.24.14via llm-release-walk
2 files changed · +51 12
  • fuzz/fuzz_rest_api.c+38 0 modified
    @@ -113,12 +113,43 @@ fuzz_rest_api_detailed_cleanup(void)
     	}
     }
     
    +static void
    +run_clients_query_regression_cases(void)
    +{
    +	static const char *cases[] = {
    +		"/api/v4/clients?proto_ver=0",
    +		"/api/v4/clients?proto_ver=6",
    +		"/api/v4/clients?clean_start=maybe",
    +		"/api/v4/clients?foo=bar",
    +		"/api/v4/clients?proto_ver=3",
    +		"/api/v4/clients?proto_ver=4",
    +		"/api/v4/clients?proto_ver=5",
    +	};
    +
    +	g_conf.http_server.auth_type = NONE_AUTH;
    +	set_http_server_conf(&g_conf.http_server);
    +
    +	nng_socket sock = { 0 };
    +	g_conf.http_server.broker_sock = &sock;
    +
    +	for (size_t i = 0; i < sizeof(cases) / sizeof(cases[0]); i++) {
    +		http_msg req = { 0 };
    +		put_http_msg(
    +		    &req, "application/json", "GET", cases[i], NULL, "", 0);
    +
    +		http_msg resp = process_request(&req, &g_conf.http_server, &sock);
    +		destory_http_msg(&resp);
    +		destory_http_msg(&req);
    +	}
    +}
    +
     int
     LLVMFuzzerInitialize(int *argc, char ***argv)
     {
     	(void) argc;
     	(void) argv;
     	ensure_inited();
    +	run_clients_query_regression_cases();
     	atexit(fuzz_rest_api_detailed_cleanup);
     	return 0;
     }
    @@ -168,6 +199,13 @@ static const char *uri_templates[] = {
     	"/logs/full",
     	"/platform_infos",
     	"/clients",
    +	"/clients?proto_ver=0",
    +	"/clients?proto_ver=6",
    +	"/clients?clean_start=maybe",
    +	"/clients?foo=bar",
    +	"/clients?proto_ver=3",
    +	"/clients?proto_ver=4",
    +	"/clients?proto_ver=5",
     	"/clients/%s",
     	"/clients/username/%s",
     	"/subscriptions",
    
  • nanomq/rest_api.c+13 12 modified
    @@ -1505,9 +1505,7 @@ get_clients(http_msg *msg, kv **params, size_t param_num,
     			               "disconnected") == 0) {
     				info.conn_state = "disconnected";
     			} else {
    -				return error_response(msg,
    -				    NNG_HTTP_STATUS_BAD_REQUEST,
    -				    REQ_PARAM_ERROR);
    +				goto bad_request;
     			}
     		} else if (strcmp(params[i]->key, "clean_start") == 0) {
     			if (nng_strcasecmp(params[i]->value, "true") == 0) {
    @@ -1518,9 +1516,7 @@ get_clients(http_msg *msg, kv **params, size_t param_num,
     				info.clean_start     = false;
     				info.has_clean_start = true;
     			} else {
    -				return error_response(msg,
    -				    NNG_HTTP_STATUS_BAD_REQUEST,
    -				    REQ_PARAM_ERROR);
    +				goto bad_request;
     			}
     		} else if (strcmp(params[i]->key, "proto_name") == 0) {
     			info.proto_name = params[i]->value;
    @@ -1529,20 +1525,25 @@ get_clients(http_msg *msg, kv **params, size_t param_num,
     			int  ver   = 0;
     			if (sscanf(params[i]->value, "%d%c", &ver, &extra) !=
     			        1 ||
    -			    ver < 0 || ver > UINT8_MAX) {
    -				return error_response(msg,
    -				    NNG_HTTP_STATUS_BAD_REQUEST,
    -				    REQ_PARAM_ERROR);
    +			    ver < 0 || ver > UINT8_MAX ||
    +			    (ver != MQTT_PROTOCOL_VERSION_v31 &&
    +			        ver != MQTT_PROTOCOL_VERSION_v311 &&
    +			        ver != MQTT_PROTOCOL_VERSION_v5)) {
    +				goto bad_request;
     			}
     			info.proto_ver     = (uint8_t) ver;
     			info.has_proto_ver = true;
     		} else {
    -			return error_response(
    -			    msg, NNG_HTTP_STATUS_BAD_REQUEST, REQ_PARAM_ERROR);
    +			goto bad_request;
     		}
     	}
     
     	nng_id_map_foreach2(pipe_id_map, get_client_cb, &info);
    +	goto out;
    +
    +bad_request:
    +	cJSON_Delete(data_info);
    +	return error_response(msg, NNG_HTTP_STATUS_BAD_REQUEST, REQ_PARAM_ERROR);
     
      out:
     
    
16bdb38921e7

* FIX [devops] fix windows yaml workflow

https://github.com/emqx/nanomqJaylinYuMay 29, 2026Fixed in 0.24.14via release-tag
1 file changed · +10 4
  • .github/workflows/windows.yml+10 4 modified
    @@ -44,12 +44,18 @@ jobs:
     
           - name: Configure
             run: |
    -          $extra_flags = ""
    -          if ("${{ matrix.arch }}" -eq "arm64") {
    -            $extra_flags = "-A ARM64"
    +          if (Test-Path build/CMakeCache.txt) {
    +            Remove-Item build/CMakeCache.txt -Force
    +          }
    +          if (Test-Path build/CMakeFiles) {
    +            Remove-Item -Recurse -Force build/CMakeFiles
               }
    -          cmake -B build -DNNG_ENABLE_SQLITE=ON -DENABLE_RULE_ENGINE=ON -DENABLE_JWT=ON -DBUILD_BENCH=ON -DCMAKE_INSTALL_PREFIX=install $extra_flags
     
    +          if ("${{ matrix.arch }}" -eq "arm64") {
    +            cmake -B build -DNNG_ENABLE_SQLITE=ON -DENABLE_RULE_ENGINE=ON -DENABLE_JWT=ON -DBUILD_BENCH=ON -DCMAKE_INSTALL_PREFIX=install -A ARM64
    +          } else {
    +            cmake -B build -DNNG_ENABLE_SQLITE=ON -DENABLE_RULE_ENGINE=ON -DENABLE_JWT=ON -DBUILD_BENCH=ON -DCMAKE_INSTALL_PREFIX=install
    +          }
           - name: Build
             run: cmake --build build --config Release --target install
     
    

Vulnerability mechanics

Root cause

"Type confusion in the QUIC dialer close path: `aio->prov_data` is stored as `nni_quic_conn*` but read as `ex_quic_conn*`."

Attack vector

An attacker who can trigger a QUIC dialer close while a dial AIO is pending will cause the close path to misinterpret the stored pointer. The PoC shows that calling `nng_stream_dialer_close` immediately after `nng_stream_dialer_dial` (without waiting for the dial to complete) leads to a hang or crash. The precondition is local access to the NanoMQ process and the ability to initiate a QUIC dialer connection [ref_id=1].

Affected code

The vulnerability resides in `nng/src/supplemental/quic/msquic_dial.c`. The write site at approximately line 525 stores an `nni_quic_conn*` pointer via `nni_aio_set_prov_data(aio, c)`, but the read site at approximately lines 582-587 retrieves it as an `ex_quic_conn*` pointer via `nni_aio_get_prov_data(aio)`, causing a type confusion.

What the fix does

The advisory does not include a patch diff, but the fix in version 0.24.14 ensures that the pointer stored in `aio->prov_data` is read with the correct type (`nni_quic_conn*`) on the close path, or that the close path no longer dereferences the provider data as `ex_quic_conn*`. This eliminates the invalid object interpretation that caused the hang/crash [ref_id=1].

Preconditions

  • inputThe attacker must be able to trigger a QUIC dialer close while a dial AIO is still pending (i.e., before the dial completes).
  • authLocal access to the NanoMQ process is required to invoke the dialer operations.

Reproduction

The advisory provides a complete PoC program that allocates a dialer, starts a dial with a 1-second timeout, immediately closes the dialer, and then frees resources. When compiled with ASAN and the QUIC library, the process hangs after printing `[PoC] close dialer now` and is terminated by a timeout watchdog [ref_id=1].

Generated on May 29, 2026. Inputs: CWE entries + fix-commit diffs from this CVE's patches. Citations validated against bundle.

References

2

News mentions

0

No linked articles in our index yet.