The Incident
CVE-2026-49950 was published on October 13, 2026 against Xorbits Xoscar — the Python actor and RPC framework that Xinference, a widely deployed self-hosted LLM, embedding, and multimodal inference server, uses between its coordinator and worker pools. The flaw stacks CWE-306 (Missing Authentication for Critical Function) on CWE-502 (Deserialization of Untrusted Data). The upstream Xoscar advisory GHSA-mmvj-m246-m9vf scores it at CVSS 4.0 base 9.3 (CVSS 3.1 base 9.8 per OSV and OpenCVE). Any attacker who can reach an actor-pool TCP port triggers cloudpickle.loads on their own bytes before the pool performs message-type dispatch or any sender check — a __reduce__ gadget then runs arbitrary Python inside the pool process. The downstream Xinference advisory GHSA-2hj6-h88x-mv4j confirms the Xinference HTTP API’s authentication does not protect those actor ports: a distributed Compose deployment publishes HTTP on 9999 alongside actor transports on 30001 and 30002. Xoscar versions 0.1.0 through 0.11.2 are unconditionally vulnerable; 0.12.0 and the subsequent 0.12.1 release ship an opt-in HMAC-SHA256 message authentication mode via XOSCAR_AUTH_KEY, but both advisories state there is no version-only remedy — the mode is off by default, and keyed and unkeyed processes cannot communicate, so the fix only lands when an operator plumbs the key across every pool, supervisor, and client. Discovery was through coordinated disclosure: Valentin Lobstein (Chocapikk) reported the flaw to Xorbits on June 8, 2026 via the VulnCheck CNA; the 120-day deadline elapsed on October 6; the CVE record and the Xoscar / Xinference GitHub advisories landed on October 13 and 16. As of the Cloud Security Alliance’s October 22 note, there is no public report of in-the-wild exploitation.
MITRE ATT&CK coverage:
The Authority Path That Failed
The identity carrying execution authority is the Xoscar actor-pool worker process. It runs as whatever OS user launched Xinference — the researcher reports that on GPU nodes operators commonly leave that user as root so the process can claim the device, though the exact user is a deployment choice, not a Xoscar requirement. The scope the pool holds is full Python inside its own process space: every file the pool user can read (model weights, HuggingFace tokens, mounted cluster secrets), every outbound network path, and in distributed mode a trust relationship back to the supervisor and to peer worker pools over the same unauthenticated transport. The scope it exercises on a crafted inbound frame is a cloudpickle.loads call on attacker-controlled bytes, delivered through the pool’s generic receive loop with no principal ever established at the transport.
The trust anchor that failed first is Xoscar’s premise that its actor ports sit on a private cluster network and the operator is responsible for making that true. Nothing in the framework ever asked the operator to authorize code execution over that transport — cloudpickle has always been a code-execution-capable deserializer, and the pool treated every inbound TCP connection as implicitly trusted. The October 16 fix leaves that trust anchor opt-in: XOSCAR_AUTH_KEY is unset by default, so pip install --upgrade xoscar xinference closes the hole only when the operator also provisions a shared key cluster-wide. The gap between held and exercised authority is detectable before an incident: any Xoscar or Xinference pool whose actor port is reachable from an untrusted CIDR and whose process environment lacks XOSCAR_AUTH_KEY is the pre-exploit evidence.
SecurityV0 Perspective
SecurityV0 would raise unproven_execution against the inference runtime on every Xinference deployment running an affected Xoscar version — including upgraded hosts that have not set XOSCAR_AUTH_KEY. The finding attaches because a code-execution path (cloudpickle over a listening TCP port) is wired into the actor runtime by default, below the layer where the deploying operator can see it, and no record exists of a human authoring the decision to accept arbitrary pickle traffic from the network. The pattern lines up with the Langflow CSV-Agent-plus-REPL and the Flowise MCP STDIO adapter precedents already in SV0’s library: the framework ships, a code-execution tool attaches itself to the runtime, the operator never explicitly consents, and a patch whose default state leaves the tool armed does not clear the finding.
The evidence pack would carry the Xoscar version, the Xinference version, every actor-pool port the host exposes (coordinator and workers), the bind interface and reachable CIDRs for each, the XOSCAR_AUTH_KEY presence check against the process environment, the OS user the pool runs as, and the manifest of loaded Xinference models. Pre-exfiltration, that pack answers: does any Xoscar actor port on this fleet accept pickle traffic without a shared HMAC key? Post-exfiltration, it answers: which source IP first contacted the actor port, under which pool PID, and what Python the pool ran between first contact and the forensic cutover — the pool’s received-frame history and the parent process’s argv are the records that make the authority boundary auditable after the fact.
What To Do
- Upgrade Xoscar to 0.12.1 and set
XOSCAR_AUTH_KEYcluster-wide, in that order. 0.12.0 and 0.12.1 are off-by-default; GHSA-mmvj-m246-m9vf and GHSA-2hj6-h88x-mv4j both state there is no version-only remedy. Generate a strong secret, place it in every pool, supervisor, and client’s environment, verify startup logs show HMAC mode engaged, and plan for a flag day — keyed and unkeyed processes cannot communicate, so a mixed-mode rolling upgrade is not an option. - Firewall the actor-pool transports off the Internet and off every network the inference server does not need. Xinference’s distributed Compose exposes actor ports 30001 and 30002 beside the HTTP API’s 9999; treat those actor ports as execution planes rather than data planes, and bind them to a dedicated cluster interface. The HTTP API’s authentication does not reach them, so a public bind on 30001 is remote code execution with no auth.
- Inventory every self-hosted inference host for Xoscar actor ports and record whether
XOSCAR_AUTH_KEYis set. Enumerate listening TCP sockets per host, map ports to Xoscar pool PIDs via/proc/<pid>/net/tcp, read/proc/<pid>/environfor the key, and flag any pool reachable from untrusted ranges without a key asunproven_executionagainst the inference runtime. The combination — reachable actor port plus missing key — is the signal; a sibling CVE in the same product line (CVE-2026-61539, Llama3 tool-calleval, fixed in Xinference 2.7.0) tells you this surface is being actively researched. - Drop pool privileges off
rootwherever the GPU runtime permits it. The authority a compromised pool inherits is the OS user that launched the server. Run Xinference under a dedicated service account, grant device access via Linux cgroups ornvidia-container-toolkitcapabilities rather than UID 0, and keep model weights and HuggingFace tokens out of that user’s home directory. The researcher’s root-shell lab result is a configuration outcome, not a Xoscar guarantee — your deployment decides which user the exploit lands as. - Collect frame-level telemetry on actor ports with enough retention to answer the post-exfiltration question. Capture
src_ip,src_port, the pool PID, the first eight bytes of each received frame (enough to distinguish a legitimate Xoscar header from a crafted pickle), and the receiving pool’sargvand environment. Retain long enough that if a PoC — Chocapikk’s or a copycat’s — hits your fleet, you can answer which source first touched the port and what Python the pool ran before you rotated the service account.
Sources
- NVD — CVE-2026-49950
- GHSA-mmvj-m246-m9vf — Xoscar repository security advisory
- GHSA-2hj6-h88x-mv4j — Xinference repository security advisory
- Xoscar v0.12.0 release notes
- Xoscar remediation commit 3ffde7e9
- VulnCheck — Xorbits Xoscar unauth cloudpickle RCE
- CSA research note — Xoscar / Xinference cloudpickle RCE (Oct 22, 2026)
- OSV — CVE-2026-49950
- MITRE ATT&CK: T1210, T1059.006