Skip to content

fix(multinode): use JSON instead of pickle for node-IP exchange to prevent RCE - #1419

Open
Anai-Guo wants to merge 1 commit into
ModelTC:mainfrom
Anai-Guo:fix-multinode-zmq-pickle-rce
Open

fix(multinode): use JSON instead of pickle for node-IP exchange to prevent RCE#1419
Anai-Guo wants to merge 1 commit into
ModelTC:mainfrom
Anai-Guo:fix-multinode-zmq-pickle-rce

Conversation

@Anai-Guo

@Anai-Guo Anai-Guo commented Aug 2, 2026

Copy link
Copy Markdown

Summary

send_and_receive_node_ip() binds a ZMQ PULL socket on tcp://*:{port} (all interfaces, 0.0.0.0) on the head node during multinode startup and reads peer data with recv_pyobj(), which is pickle.loads() on the raw socket bytes. Any host that can reach the port can send a crafted pickle payload and achieve arbitrary code execution on the head node — the same class of issue as CVE-2025-32444 in vLLM. Closes #1413.

Fix

The only value exchanged is the child node's IP string, which is fully JSON-serializable, so this switches the send_pyobj/recv_pyobj pair to send_json/recv_json. This removes the pickle deserialization sink at the network boundary with no behavioral change (a string round-trips identically through JSON).

-                args.child_ips.append(comm_socket.recv_pyobj())
+                args.child_ips.append(comm_socket.recv_json())
...
-            comm_socket.send_pyobj(local_ip)
+            comm_socket.send_json(local_ip)

Note on the diff base

My fork's main trailed upstream and I don't hold the workflow OAuth scope, so I couldn't rebase this branch directly onto the current main tip (upstream has since refactored the surrounding base_port lines in this same function). GitHub may therefore flag a small merge conflict confined to the two adjacent lines — it resolves trivially by keeping upstream's base_port lines and applying only the recv_json/send_json change. Happy to hand the patch over in whatever form is easiest, or a maintainer can cherry-pick the two-line change onto tip.

🤖 Generated with Claude Code

…event RCE

send_and_receive_node_ip() bound a ZMQ PULL socket on tcp://*:PORT (all
interfaces) on the head node and read peer data with recv_pyobj(), which
calls pickle.loads() on the wire bytes. Any host able to reach the port
could send a crafted pickle payload and achieve arbitrary code execution
on the head node, the same class of issue as CVE-2025-32444 in vLLM.

The only value exchanged is the child node's IP string, which is fully
JSON-serializable, so switch the send/recv pair to send_json/recv_json.
This removes the pickle deserialization sink without changing behavior.

Closes ModelTC#1413
@gemini-code-assist

Copy link
Copy Markdown
Contributor

Caution

The consumer version of Gemini Code Assist on GitHub has been sunset. All code review activity has officially ceased.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

[BUG]Unauthenticated pickle deserialization via ZMQ wildcard bind in multinode startup → RCE

1 participant