CVE-2021-20234
Description
ZeroMQ client before 4.3.3 has a memory leak in pipe.cpp that allows malicious servers to crash the client, impacting availability.
AI Insight
LLM-synthesized narrative grounded in this CVE's description and references.
ZeroMQ client before 4.3.3 has a memory leak in pipe.cpp that allows malicious servers to crash the client, impacting availability.
Vulnerability
A memory leak exists in src/pipe.cpp of ZeroMQ (libzmq) versions before 4.3.3. When a pipe processes a delimiter while not in an active state but still has an unfinished message, the message is leaked. This issue is triggered when a client connects to a malicious or compromised server that does not use CURVE/ZAP encryption. The vulnerability was discovered via oss-fuzz [1][2].
Exploitation
An attacker must operate a server that the client connects to without CURVE/ZAP encryption. The attacker sends crafted byte sequences (e.g., fffefefd0101010108ff01010108ff010101010101c0a15b...) that cause the client to repeatedly allocate memory without freeing it. The leak can be reproduced with specific inputs, leading to gradual memory exhaustion. The attacker does not need any prior authentication or privileges [2].
Impact
The uncontrolled memory consumption causes the client process to crash after multiple connections, resulting in a denial of service. The highest threat is to system availability. No data is compromised, but the client becomes unavailable [1][2].
Mitigation
Patched in version 4.3.3 via pull request #3918 [2]. Users should upgrade to 4.3.3 or later. No workarounds are available, although using CURVE/ZAP encryption may limit exposure to trusted servers. Red Hat's Bugzilla marks the issue as closed WONTFIX for some affected components, but upgrading is the recommended action [1][2].
AI Insight generated on May 24, 2026. Synthesized from this CVE's description and the cited reference URLs; citations are validated against the source bundle.
Affected products
2- ZeroMQ/ZeroMQ clientdescription
Patches
204f5bbedee58Finalize changelog for 4.3.3
1 file changed · +1 −1
NEWS+1 −1 modified@@ -1,4 +1,4 @@ -0MQ version 4.3.x stable, released on 20xx/xx/xx +0MQ version 4.3.3 stable, released on 2020/09/07 ================================================ * Security advisories:
303311264522Merge pull request #3918 from bluca/fuzzers
3 files changed · +34 −2
src/pipe.cpp+1 −0 modified@@ -506,6 +506,7 @@ void zmq::pipe_t::process_delimiter () if (_state == active) _state = delimiter_received; else { + rollback (); _out_pipe = NULL; send_pipe_term_ack (_peer); _state = term_ack_sent;
tests/test_bind_curve_fuzzer.cpp+24 −1 modified@@ -42,6 +42,10 @@ extern "C" int LLVMFuzzerTestOneInput (const uint8_t *data, size_t size) { const char *fixed_client_public = "{{k*81)yMWEF{/BxdMd[5RL^qRFxBgoL<8m.D^KD"; + const char *fixed_client_secret = + "N?Gmik8R[2ACw{b7*[-$S6[4}aO#?DB?#=<OQPc7"; + const char *fixed_server_public = + "3.9-xXwy{g*w72TP*3iB9IJJRxlBH<ufTAvPd2>C"; const char *fixed_server_secret = "T}t5GLq%&Qm1)y3ywu-}pY3KEA//{^Ut!M1ut+B4"; void *handler; @@ -86,8 +90,27 @@ extern "C" int LLVMFuzzerTestOneInput (const uint8_t *data, size_t size) sent = send (client, (const char *) data, size, MSG_NOSIGNAL); msleep (250); - close (client); + // Drain the queue, if any + zmq_msg_t msg; + zmq_msg_init (&msg); + while (-1 != zmq_msg_recv (&msg, server, ZMQ_DONTWAIT)) { + zmq_msg_close (&msg); + zmq_msg_init (&msg); + } + + // A well-behaved client should work while the malformed data from the other + // is being received + curve_client_data_t curve_client_data = { + fixed_server_public, fixed_client_public, fixed_client_secret}; + void *client_mon; + void *client_good = create_and_connect_client ( + my_endpoint, socket_config_curve_client, &curve_client_data, &client_mon); + bounce (server, client_good); + + close (client); + test_context_socket_close_zero_linger (client_good); + test_context_socket_close_zero_linger (client_mon); shutdown_context_and_server_side (zap_thread, server, server_mon, handler); teardown_test_context ();
tests/test_bind_null_fuzzer.cpp+9 −1 modified@@ -49,6 +49,11 @@ extern "C" int LLVMFuzzerTestOneInput (const uint8_t *data, size_t size) bind_loopback_ipv4 (server, my_endpoint, sizeof (my_endpoint)); fd_t client = connect_socket (my_endpoint); + void *client_good = test_context_socket (ZMQ_SUB); + TEST_ASSERT_SUCCESS_ERRNO ( + zmq_setsockopt (client_good, ZMQ_SUBSCRIBE, "", 0)); + TEST_ASSERT_SUCCESS_ERRNO (zmq_connect (client_good, my_endpoint)); + // If there is not enough data for a full greeting, just send what we can // Otherwise send greeting first, as expected by the protocol uint8_t buf[64]; @@ -64,8 +69,11 @@ extern "C" int LLVMFuzzerTestOneInput (const uint8_t *data, size_t size) sent = send (client, (const char *) data, size, MSG_NOSIGNAL); msleep (250); - close (client); + TEST_ASSERT_EQUAL_INT (6, zmq_send_const (server, "HELLO", 6, 0)); + TEST_ASSERT_EQUAL_INT (6, zmq_recv (client_good, buf, 6, 0)); + close (client); + test_context_socket_close_zero_linger (client_good); test_context_socket_close_zero_linger (server); teardown_test_context ();
Vulnerability mechanics
Root cause
"Missing `rollback()` call in `pipe_t::process_delimiter()` when the pipe is not in active state but still has an unfinished message, causing the message to be leaked."
Attack vector
A malicious or compromised server sends crafted protocol data (a delimiter) to a ZeroMQ client that is not using CURVE/ZAP authentication [ref_id=1]. The client's pipe processes this delimiter while in a non-active state with an unfinished message, causing a memory leak. An attacker who controls multiple servers can cause the client to connect to them, and the leak scales with the number of such servers, potentially exhausting client memory and causing a crash [ref_id=1]. The advisory notes this is "probably very hard to exploit in a real application, since you'd need to trick the client into connecting to multiple servers" [ref_id=1].
Affected code
The vulnerability is in `src/pipe.cpp` in the `zmq::pipe_t::process_delimiter()` method. When a pipe processes a delimiter and is already not in the `active` state but still has an unfinished message, the message is leaked because `rollback()` was not called before sending the pipe term acknowledgment [patch_id=2271410].
What the fix does
The patch adds a call to `rollback()` in `src/pipe.cpp` before `_out_pipe` is set to NULL and the pipe term acknowledgment is sent [patch_id=2271410]. This ensures that any unfinished message is properly rolled back (its resources freed) when the pipe processes a delimiter while not in the active state, preventing the memory leak. The fuzz tests were also updated to verify that a well-behaved client can still communicate correctly while malformed data is being received [patch_id=2271410].
Preconditions
- configClient must connect to a malicious or compromised server without CURVE/ZAP authentication
- inputServer sends crafted delimiter data that triggers the non-active state path in pipe processing
- networkMultiple such servers may be needed to exhaust client memory (leak scales with number of servers)
Generated on May 24, 2026. Inputs: CWE entries + fix-commit diffs from this CVE's patches. Citations validated against bundle.
References
2- bugzilla.redhat.com/show_bug.cgimitrex_refsource_MISC
- github.com/zeromq/libzmq/security/advisories/GHSA-wfr2-29gj-5w87mitrex_refsource_MISC
News mentions
0No linked articles in our index yet.