diff --git a/src/controller/cluster.c b/src/controller/cluster.c index 8ca858d..54d3fd5 100644 --- a/src/controller/cluster.c +++ b/src/controller/cluster.c @@ -247,6 +247,23 @@ static int myreq_handler(vtask_t* task) init_reply_by_req(&reply, &request); + /* bug-1286: a chaotic-restart race can run this handler on an inbound endpoint + * whose owner (cluster_t) is NULL — not yet wired at accept time, or cleared + * during teardown. With c == NULL, `&c->ha` is a NULL+offset garbage pointer + * (0x3ff0) that SIGSEGV'd ha_dispatch_gate (ha.c) when it loaded ha->role. + * Drop the request LOUDLY instead of dereferencing it: we have no cluster + * context to dispatch through and cannot even address a reply (it needs + * c->myhost). The controller stays alive (never self-terminate on an + * operational condition); the peer retries once the endpoint is owned. The + * request is freed here so the decoded packet does not leak. */ + if (c == NULL) { + WARN("myreq_handler: endpoint %p has no owner (cluster) — dropping %s " + "(restart race, bug-1286)", (void*)ep, + xce_request_op_name(&request)); + xce_free_request(&request); + return -1; + } + /* Dispatch gate: reject ops not allowed in current FSM state (req 1.3, 1.4) */ rc = ha_dispatch_gate(&c->ha, request.opcode); if (rc != 0) {