fix(scheduler): release interned queue resreq with dereference, not free (bug-1858)
q->resreq holds an interned makeString() value (pointer past the xstring object header). core_upsert_queue() and sched_update_existing_queue() released the old value with FREEUP()/free() when a queue update carried a new resreq, an ASAN bad-free. Seen as HA soak replay-parity errors=1: the sim replay of a snapshot-restored queue followed by a config push aborted in core_upsert_queue. Both sites now dereference() the old value (NULL-guarded). Regression tests cover each site; the existing queue-update test had skipped the resreq branch. Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
This commit is contained in:
parent
441008cd30
commit
9e9f2798a0
4 changed files with 65 additions and 2 deletions
|
|
@ -457,6 +457,40 @@ test_push_events_fires_each_hook_once_per_event_with_picker_idle(void)
|
|||
TEST_ASSERT_EQUAL_PTR(&rusages, sched_get_job(&core, 42)->rusages);
|
||||
}
|
||||
|
||||
/* bug-1858: a QUEUE_UPDATE carrying a resreq for an existing queue whose
|
||||
* resreq is already set must dereference() the old interned makeString()
|
||||
* value, not free() it (ASAN bad-free in core_upsert_queue, first seen
|
||||
* replaying an HA-soak recording). */
|
||||
static void
|
||||
test_queue_update_replaces_existing_interned_resreq(void)
|
||||
{
|
||||
xce_sched_core_t core;
|
||||
xce_core_event_t ev;
|
||||
xce_queue_t wire_queue;
|
||||
xce_sched_queue_t* q;
|
||||
|
||||
fixture_init(&core, &g_full_plugin);
|
||||
|
||||
memset(&wire_queue, 0, sizeof(wire_queue));
|
||||
wire_queue.queue = "normal";
|
||||
wire_queue.status = QUEUE_STAT_OPEN;
|
||||
|
||||
memset(&ev, 0, sizeof(ev));
|
||||
ev.type = XCE_CORE_EVENT_QUEUE_UPDATE;
|
||||
ev.u.queue_update.queue = &wire_queue;
|
||||
|
||||
/* create, then set a resreq, then replace it */
|
||||
TEST_ASSERT_EQUAL_INT(0, xce_core_push_events(&core, &ev, 1));
|
||||
wire_queue.resreq = "type == any";
|
||||
TEST_ASSERT_EQUAL_INT(0, xce_core_push_events(&core, &ev, 1));
|
||||
wire_queue.resreq = "type == local";
|
||||
TEST_ASSERT_EQUAL_INT(0, xce_core_push_events(&core, &ev, 1));
|
||||
|
||||
q = sched_get_queue(&core, "normal");
|
||||
TEST_ASSERT_NOT_NULL(q);
|
||||
TEST_ASSERT_EQUAL_STRING("type == local", q->resreq);
|
||||
}
|
||||
|
||||
/* JOBS_PUSH with njobs > 1: on_job_push must fire once PER JOB, not once
|
||||
* per event -- Req 5 AC 4's "on_job_push(picker, job) for each job in the
|
||||
* batch". */
|
||||
|
|
@ -1849,6 +1883,7 @@ int main(void)
|
|||
UNITY_BEGIN();
|
||||
RUN_TEST(test_push_events_fires_each_hook_once_per_event_with_picker_idle);
|
||||
RUN_TEST(test_jobs_push_fires_on_job_push_once_per_job);
|
||||
RUN_TEST(test_queue_update_replaces_existing_interned_resreq);
|
||||
RUN_TEST(test_jobs_push_duplicate_jobid_does_not_call_on_job_push_or_leak);
|
||||
RUN_TEST(test_jobs_push_duplicate_jobid_with_newer_state_updates_existing_job);
|
||||
RUN_TEST(test_jobs_push_duplicate_jobid_run_state_transfers_allocation);
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue