rpc : reject invalid top-level graph nodes in graph_compute - #25670
liminfei-amd wants to merge 1 commit into
Conversation
91b62a7 to
3900550
Compare
id 0 is a valid "no tensor" sentinel for optional sources, not for a top-level graph node. Reject any failed node reconstruction regardless of id, before running the graph. Fixes ggml-org#25299 Reported-by: professor-moody Assisted-by: GitHub Copilot
3900550 to
181b199
Compare
|
@ruixiang63 you reopened this PR back in July — would you be able to take a look, Rebased onto current master. The branch had gone stale and was conflicting. The I also removed a branch that my own change had made dead: once the guard returns The crash still reproduces on current master. Same test harness on both sides,
One thing I cannot do from here: the workflow runs on this head are all |
|
Independent reproduction — still reproducible at d1d3c33 Confirming this NULL-pointer dereference is still live on the current tree. I reproduced it independently on commit Root cause rpc_server::deserialize_tensor (ggml/src/ggml-rpc/ggml-rpc.cpp, ~line 1370) copies the attacker-controlled op, A single 316-byte unauthenticated GRAPH_COMPUTE message (one tensor, op=GGML_OP_ADD, no src tensors) crashes the whole Reproduction Build (RPC enabled): git checkout d1d3c33 A. In-process (ground truth) — drives the raw blob through the public rpc_server::graph_compute(): [08b] blob=s07_op_nosrc.bin size=316 B. Remote / TCP — against the real ggml-rpc-server: LD_LIBRARY_PATH=$PWD/build-rpc/bin ./build-rpc/bin/ggml-rpc-server --host 127.0.0.1 --port 19040 The client completes a HELLO handshake (cmd=14, 24-byte conn_caps), then sends GRAPH_COMPUTE (cmd=10, payload = the HELLO rsp size=28 Server result — process dies with SIGSEGV, port freed: [1]+ Segmentation fault (core dumped) ...ggml-rpc-server --host 127.0.0.1 --port 19040 Server log stops at Accepted client connection — the crash happens inside graph_compute before any response is Trigger payload s07_op_nosrc.bin — sha256 = 086dd889364fe7b26c1a35709aaecf8ccc1f5109f2f8ddfbc4ae3b87dcc285e8 Environment
Impact Remote unauthenticated denial-of-service: one crafted 316-byte message to the RPC server's TCP port (default 1234) Note (scope): RPC is an opt-in feature — it requires a GGML_RPC=ON build and an operator explicitly starting This is an independent confirmation, not a new report — the sink is already tracked as CVE-2026-78148 / issue #25299. |
Overview
rpc_server::graph_compute()deserializes each top-level graph node id andresolves it through
create_node(). The old check only rejected anullptrresult when the wire id was non-zero, treating a
nullptrresult for id0as "expected" and letting it into
graph->nodes. id0is the wire sentinelfor "no tensor" and is valid for optional tensor sources (
src[]/view_src),but it is not a valid top-level graph node. The backend later dereferences
that node during graph planning, crashing the RPC server on a single crafted
GRAPH_COMPUTEmessage (device=0, n_nodes=1, node_id=0, n_tensors=0).Fixes #25299.
Reject every failed top-level node reconstruction before calling
ggml_backend_graph_compute(), regardless of id.Additional information
Verified with a localhost-only RPC client sending the malformed message above
against a CPU-only build (no ROCm/HIP): before the fix the server exits with
SIGSEGV; after the fix the malformed connection is closed and a subsequent
client can still connect and complete
HELLOnormally.A regression test (
test-rpc-invalid-graph-node) is included; it starts theRPC server, sends the crafted message, confirms the connection is dropped, and
verifies the server stays alive for new connections.
Requirements
create_node/graph_computecode, reproduced the crash and the fix's effect with a local RPC client, and can explain every line of the change.