fix(controller): string resvals own their bytes -- rejoin use-after-free killed new primary (bug-1851)

update_val_res() memcpy'd the xce_val_t into w->host.resvals, so the
"type"/"model" string resources aliased the interned xstrings owned by
w->host.attrs. rejoin_worker() drops the old attrs strings, then
apply_join_state() -> resync_arm_round() resolves the previous join's
still-outstanding round, firing the worker hooks before
add_hostattrs_resvals() re-seeds resvals; the apiserver hook's
collect_resval() strdup()ed the freed hostmodel. Live: a newly promoted
primary died ~8s after election during an HA soak.

A string resval now stores a private strdup (taken before the old copy is
freed), and the owning arrays free their entries: host.resvals gets
kill_resval (replacing the FIXME NULL killer) and c->sharedinst's
resval_obj_killer (previously a no-op) frees the string and the value --
both also leaked every xce_val_t before. Also closes the same alias for
worker-reported string resvals and config values.

Regression test: test_rejoin_worker_string_resvals_survive_outstanding_round
(RED reproduced the live ASAN stack; GREEN after). meson test 109/109.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
This commit is contained in:
Rongsong Shen 2026-09-24 08:41:55 +00:00
parent ac033f4783
commit ca4db13a79
No known key found for this signature in database
GPG key ID: 5BCDAAE0123E9FE2
5 changed files with 199 additions and 4 deletions

View file

@ -22,6 +22,21 @@ static size_t worker_datasize(va_list ap)
return sz;
}
/* bug-1851: host.resvals owns each xce_val_t (update_val_res, controller
* worker.c) and, for a string value, a private strdup()ed copy. */
static void kill_resval(void* p)
{
xce_val_t* v = (xce_val_t*)p;
if (v == NULL) {
return;
}
if (v->which == Value_u_svalue) {
FREEUP(v->u.svalue);
}
free(v);
}
static void kill_worker(worker_t* w)
{
dereference(w->host.name);
@ -66,8 +81,7 @@ static int copy_worker(worker_t* dst, xce_worker_t* src)
dst->host.attrs->hostmodel = makeString(src->attrs->hostmodel);
dst->host.status = src->status;
/* FIXME: */
zarray_init(&(dst->host.resvals), 64, NULL);
zarray_init(&(dst->host.resvals), 64, kill_resval);
dst->host.dead_since = src->dead_since;
dst->host.pending_ctrl = src->pending_ctrl;