fix(libxce): bug-1600 -- makeGroup() freed rtti makeString() pointer via raw free()

makeGroup()'s existing-group update path (hit whenever a config-push
re-applies a group name that already has members, e.g. a retried config
push during controller failover) released xce_g_member_t.u.name via
FREEUP() (plain free()). u.name is allocated by makeString(), an
rtti-managed, refcounted pointer that is not a malloc()-base address --
freeing it directly corrupts the heap. group_killer() in the same file
already releases the identical field correctly via dereference().

Switch both FREEUP(m->u.name) call sites to dereference(m->u.name) to
match. Add a regression test that re-applies config for the same group
name twice, reproducing the exact ASAN bad-free at group.c:234 pre-fix.

This is the root cause of the reproducible xce_scheduler
"free(): invalid pointer" SIGABRT crashes seen during 3-attempt
controller-failover config-push retries (8 confirmed core dumps).

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
This commit is contained in:
Rongsong Shen 2026-08-20 04:06:05 +00:00
parent 6522a73459
commit ddee884c1b
No known key found for this signature in database
GPG key ID: 5BCDAAE0123E9FE2
4 changed files with 62 additions and 3 deletions

View file

@ -231,7 +231,7 @@ xce_group_t* makeGroup(var mgr, xce_group_config_t* config,
continue;
}
if (m->is_group == 0) {
FREEUP(m->u.name);
dereference(m->u.name);
}
FREEUP(m);
@ -244,7 +244,7 @@ xce_group_t* makeGroup(var mgr, xce_group_config_t* config,
continue;
}
if (m->is_group == 0) {
FREEUP(m->u.name);
dereference(m->u.name);
}
FREEUP(m);

View file

@ -255,12 +255,51 @@ static void test_nest2(void)
destroy(mgr);
}
static void test_reconfigure_same_group_members(void)
{
var mgr;
xce_group_config_t gdata1 = {
.name = "g1",
.members = { .count = 1, .data = (char*[]) { "rshen" } }
};
xce_group_config_t gdata2 = {
.name = "g1",
.members = { .count = 1, .data = (char*[]) { "alice" } }
};
xce_group_t* g = NULL;
int rc = 0;
groupmgr_init(&mgr, 16, 16);
g = makeGroup(mgr, &gdata1, NULL);
TEST_ASSERT_TRUE(g != NULL);
/* Re-applying config for the SAME group name (e.g. a repeated config
* push) exercises makeGroup()'s existing-group update path, which must
* release the old members without corrupting the heap. */
g = makeGroup(mgr, &gdata2, NULL);
TEST_ASSERT_TRUE(g != NULL);
rc = is_in_group(g, "alice");
TEST_ASSERT_TRUE(rc != 0);
rc = is_in_group(g, "rshen");
TEST_ASSERT_TRUE(rc == 0);
destroy(mgr);
}
void test_group(void)
{
printf("\n>> group test\n\n");
xstring_table_init(139);
RUN_TEST(test_all);
RUN_TEST(test_empty);
RUN_TEST(test_all_1);
@ -271,6 +310,7 @@ void test_group(void)
RUN_TEST(test_exclude);
RUN_TEST(test_nest);
RUN_TEST(test_nest2);
RUN_TEST(test_reconfigure_same_group_members);
xstring_table_fini();
}